#include <stdio.h>
#include <string.h>

#include <arpa/inet.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/udp.h>

#include <unistd.h>

#include <pcap/pcap.h>

int get_mac_total(uint8_t *macp){  // 计算mac地址的所有位置上字节数值
    int total = 0;
    for(int i=0;i<6;i++){
        total += macp[i];
    }
    return total;
}


void show_data(const u_char *buf, struct pcap_pkthdr *hdr){
    struct ether_header *mac = NULL;  // mac首部
    struct iphdr  *ip = NULL;  // ip 首部
    struct udphdr *udp = NULL;  // udp首部

    char destmac[18] = ""; 
    char srcmac[18] = ""; 

    char destip[16] ="";  // 目标IP
    char srcip[16] = ""; // 源IP

    if(hdr->caplen > 0){
            // 拆分MAC首部
            mac = (struct ether_header *)buf;  // 将buf中的前14个字节转化为结构体的成员数据
            if(get_mac_total(mac->ether_dhost) == 0 || get_mac_total(mac->ether_shost) == 0) return;
            sprintf(destmac, "%02x:%02x:%02x:%02x:%02x:%02x", mac->ether_dhost[0],mac->ether_dhost[1],mac->ether_dhost[2],mac->ether_dhost[3],mac->ether_dhost[4],mac->ether_dhost[5]);
            sprintf(srcmac, "%02x:%02x:%02x:%02x:%02x:%02x", mac->ether_shost[0],mac->ether_shost[1],mac->ether_shost[2],mac->ether_shost[3],mac->ether_shost[4],mac->ether_shost[5]);
            // printf("%s->%s(0x%04x)\n", srcmac, destmac,  ntohs(mac->ether_type));

            // 拆分IP首部
            if(mac->ether_type == htons(0x0800)){
                ip = (struct iphdr *)(buf+14);
                inet_ntop(AF_INET, &ip->saddr, srcip, 16);
                inet_ntop(AF_INET, &ip->daddr, destip, 16);

                printf("%s(%s)->%s(%s) protocol(%d)\n", srcmac,srcip, destmac, destip, ip->protocol);

                // 拆分UDP
                if(ip->protocol == 17){
                    // buf+ 14 + ip首部长度*4
                    udp = (struct udphdr *)(buf+14+ ip->ihl*4);

                    //udp 的数据位置:    buf+ 14 + ip首部长度*4 + 8
                    const u_char *data = buf+14+ ip->ihl*4+8;
                    printf("%s(%s:%d)->%s(%s:%d): %s\n", srcmac,srcip,ntohs(udp->source), destmac, destip, ntohs(udp->dest), data );
                }

            }
        }
}

int main(int argc, char const *argv[])
{
    // 1. 获取网卡
    char *dev = pcap_lookupdev(NULL);
    printf("网卡名: %s\n", dev);

    // 2. 打开网络设备
    pcap_t *cap = pcap_open_live(dev, 1518, 0, -1, NULL);
    if(cap == NULL) {
        perror("open live");
        return -1;
    }
    printf("--ok--\n");

    // 3. 定义子网掩码（为了编译过滤规则）
    uint32_t submask = inet_addr("255.255.255.0");

    // 4. 定义过滤规则(BPF)
    // dst 表示目标， src 表示源
    // port 表示端口号，  host 表示主机 ether表示mac表示
    // udp 协议名，还支持： tcp , http, dns,icmp,arp
    // 逻辑关系： and == &&, or == ||, not == !
    char rules[] = "udp && dst port 9000";
    struct bpf_program program;
    if(pcap_compile(cap, &program, rules, 0, submask) != 0){
        printf("规则编译失败,请检查规则格式是否正确!\n%s\n", rules);
        return -1;
    }
    if(pcap_setfilter(cap, &program) != 0){
        printf("规则编译失败,请检查规则格式是否正确!\n%s\n", rules);
        return -1;
    }
    printf("--规则正确--\n");
    while(1){ 
        struct pcap_pkthdr hdr;
        const u_char *buf = pcap_next(cap, &hdr);
        if(buf != NULL){
            printf("-----recv----\n");
            show_data(buf, &hdr);
        }
        sleep(1);
    }

    pcap_close(cap); // 关闭网络设置

    return 0;
}
